/*
*  Copyright (C) 2009  Peter Kist & Jan Ripke
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "MediatorImpl.h"
#include <mediator/Consumer.h>
#include <mediator/Producer.h>
#include <mediator/Interface.h>
#include <mediator/Request.h>

namespace galaxy {
    namespace mediator {
        namespace detail {

            MediatorImpl::MediatorImpl(void)
            {
            }

            MediatorImpl::~MediatorImpl(void)
            {
                InterestMap::iterator iInterestMap = m_InterestedConsumers.begin ();
                while (iInterestMap != m_InterestedConsumers.end ()) {
                    ConsumerList * list = (*iInterestMap).second;
                    delete list;
                    iInterestMap++;
                }
                m_InterestedConsumers.clear ();

                m_Consumers.clear ();
                m_Producers.clear ();
            }

            /** Registers a Producer. The producer's category is used as the key for lookups. This implies that
            * a producer can only be registered once. Any attempt to add a second with the same key will override
            * the previous one.
            * @param producer The producer to register.
            * @return true upon success, false upon failure
            */
            bool MediatorImpl::registerProducer(Producer& producer) {
                m_Producers[(int) producer.category()] = &producer;
                return true;
            }

            /** Unregister the specified producer. The producer is identified by category.
            * @param category The category of the producer to register.
            * @return true upon success, false upon failure
            */
            bool MediatorImpl::unregisterProducer (int category) {
                if (m_Producers[category]) {
                    m_Producers.erase (category);
                    return true;
                }
                return false;
            }

            /** registers a consumer. Producers can only send events to registered consumers.
            The consumer is registered by \b consumerType and can only be registered once per consumer type. 
            @param consumer The consumer to register
            @return true upon success, false upon failure
            */
            bool MediatorImpl::registerConsumer (Consumer& consumer) {
                // overwrite registration if 'consumerType' was already registered
                m_Consumers[std::string(consumer.consumerType())] = &consumer;
                return true;
            }

            /** unregister the consumer from the mediator.
            Also removes all interest of the consumer from the mediator.
            @param consumerType the type of the consumer to be removed.
            @return true upon success, false upon failure
            */
            bool MediatorImpl::unregisterConsumer (std::string& consumerType) {
                if (m_Consumers[consumerType]) {
                    m_Consumers.erase (consumerType);
                    return true;
                }
                return false;
            }

            bool MediatorImpl::registerInterest (std::string& consumerType, Identifier& topic) {
                Consumer * consumer = m_Consumers[consumerType];
                if (consumer) {
                    ConsumerList * consumerList = m_InterestedConsumers[(int) topic];
                    if (! consumerList) {
                        consumerList = new ConsumerList ();
                        m_InterestedConsumers[(int)topic] = consumerList;
                    }
                    consumerList->push_back (consumer);
                    return true;
                }
                return false;
            }

            bool MediatorImpl::postEvent(Event& event, PostMethod postMethod) {
                ConsumerList * consumers;

                int value = (int) event;

                // do not process 'full' consumers for non subtype events
                if (event.subtype() != Identifier::any) {
                    consumers = m_InterestedConsumers[value];
                    if (consumers && consumers->size() > 0) {
                        notifyConsumers (*consumers, event, postMethod);
                    }
                }

                // category and type match?
                consumers = m_InterestedConsumers[value & 0xffff0000];
                if (consumers && consumers->size() > 0) {
                    notifyConsumers (*consumers, event, postMethod);
                }

                // category matches ?
                consumers = m_InterestedConsumers[value & 0xff000000];
                if (consumers && consumers->size() > 0) {
                    notifyConsumers (*consumers, event, postMethod);
                }

                return true;
            }

            bool MediatorImpl::RequestProducer(const Request &Request, RequestResultPtr& result) {
                Producer * producer = m_Producers[(int)Request.category ()];
                if (producer) {
                    producer->handleRequest (Request, result);
                    return true;
                }
                return false;
            }

            /** Notify the specified list of consumers of the event. In case of POST_IMMEDIATE the event will be
            * handled by the consumer immediately (by the caller's thread). In case of POST_DELAYED the event
            * will be pushed onto the consumer's event queue after which (most likely) another thread will start
            * polling that queue.
            * \param consumerList A list of consumers to which the event will be sent (or queued)
            * \param event The event that must be sent to the consumers
            * \param postMethod Either POST_IMMEDIATE or POST_DELAYED
            */
            void MediatorImpl::notifyConsumers (ConsumerList& consumerList, Event& event, PostMethod postMethod) {
                ConsumerList::iterator i = consumerList.begin ();
                while (i != consumerList.end ()) {
                    if (postMethod == POST_IMMEDIATE) {
                        (*i)->handleEvent (event);
                    } else {
                        (*i)->pushEvent(event);
                    }

                    i++;
                }
            }

        } // detail
    } // mediator
} // galaxy
